//
//  FXButton.swift
//  FXControls
//
//  Created by fanxx on 2022/8/25.
//

import UIKit

open class FXButton: UIButton {
    public enum Position {
        case left
        case right
        case top
        case bottom
    }
    ///图片位置
    open var imagePosition: Position = .left
    ///下划线宽度
    open var underlineWidth: CGFloat = 0
    ///下划线颜色
    open var underlineColor: UIColor? = nil
    ///下划线间距: bottom无效
    open var underlineInsets: UIEdgeInsets = .zero
    ///点击区域扩大
    open var hitEdgeInsets: UIEdgeInsets = .zero
    
    func currentUnderlineColor() -> UIColor? {
        return self.underlineColor ?? self.currentTitleColor
    }
    private var __usingSystemTitleRect = false
    /// 计算内容(图片加文字)占用的大小
    private func contentBounds(for maxSize: CGSize? = nil) -> (imageFrame: CGRect, titleFrame: CGRect, contentSize: CGSize) {
        let imageSize = self.currentImage?.size ?? .zero
        var imageRect: CGRect = .init(origin: .zero, size: imageSize)
        if imageSize != .zero {
            imageRect.size.width += self.imageEdgeInsets.left + self.imageEdgeInsets.right
            imageRect.size.height += self.imageEdgeInsets.top + self.imageEdgeInsets.bottom
        }
        let titleSize: CGSize
        let boundingSize: CGSize
        if var maxSize = maxSize {
            maxSize.width -= self.titleEdgeInsets.left + self.titleEdgeInsets.right
            maxSize.height -= self.titleEdgeInsets.top + self.titleEdgeInsets.bottom
            switch self.imagePosition {
            case .top, .bottom: maxSize.height -= imageRect.size.height
            case .left, .right: maxSize.width -= imageRect.size.width
            }
            boundingSize = maxSize
        } else {
            boundingSize = .zero
        }
        self.__usingSystemTitleRect = true
        let font = self.titleLabel?.font
        let lineBreakMode = self.titleLabel?.lineBreakMode
        self.__usingSystemTitleRect = false
        if let title = self.currentAttributedTitle {
            let newText = NSMutableAttributedString(attributedString: title)
            if let font = font {
                newText.addAttribute(.font, value: font, range: .init(location: 0, length: title.length))
            }
            if let lineBreak = lineBreakMode {
                let paragraphStyle = NSMutableParagraphStyle()
                paragraphStyle.lineBreakMode = lineBreak
                newText.addAttribute(.paragraphStyle, value: paragraphStyle, range: .init(location: 0, length: title.length))
            }
            titleSize = title.boundingRect(with: boundingSize, options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil).size
        }else if let title = self.currentTitle {
            var attr: [NSAttributedString.Key: Any] = [:]
            if let font = font {
                attr[.font] = font
            }
            let lineBreak = lineBreakMode ?? .byWordWrapping
            if lineBreak != .byWordWrapping {
                let paragraphStyle = NSMutableParagraphStyle()
                paragraphStyle.lineBreakMode = lineBreak
                attr[.paragraphStyle] = paragraphStyle
            }
            titleSize = (title as NSString).boundingRect(with: boundingSize, options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: attr, context: nil).size
        } else {
            titleSize = .zero
        }
        var titleRect: CGRect = .init(origin: .zero, size: titleSize)
        if titleSize != .zero {
            titleRect.size.width += self.titleEdgeInsets.left + self.titleEdgeInsets.right
            titleRect.size.height += self.titleEdgeInsets.top + self.titleEdgeInsets.bottom
        }
        var contentSize: CGSize
        switch self.imagePosition {
        case .top, .bottom:
            contentSize = .init(width: max(imageRect.width, titleRect.width), height: imageRect.height + titleRect.height)
            imageRect.origin.x = (contentSize.width - imageRect.width) / 2
            titleRect.origin.x = (contentSize.width - titleRect.width) / 2
        case .left, .right:
            contentSize = .init(width: imageRect.width + titleRect.width, height: max(imageRect.height, titleRect.height))
            imageRect.origin.y = (contentSize.height - imageRect.height) / 2
            titleRect.origin.y = (contentSize.height - titleRect.height) / 2
        }
        switch self.imagePosition {
        case .top: titleRect.origin.y = imageRect.height
        case .bottom: imageRect.origin.y = titleRect.height
        case .left: titleRect.origin.x = imageRect.width
        case .right: imageRect.origin.x = titleRect.width
        }
        imageRect.origin.x += self.imageEdgeInsets.left
        imageRect.origin.y += self.imageEdgeInsets.top
        imageRect.size = imageSize
        titleRect.origin.x += self.titleEdgeInsets.left
        titleRect.origin.y += self.titleEdgeInsets.top
        titleRect.size = titleSize
        
        return (imageRect, titleRect, contentSize)
    }
    open override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
        if self.__usingSystemTitleRect {
            return super.titleRect(forContentRect: contentRect)
        }
        let contentBounds = self.contentBounds(for: contentRect.size)
        var rect = contentBounds.titleFrame
        rect.origin.x += contentRect.origin.x
        rect.origin.y += contentRect.origin.y
        switch self.contentHorizontalAlignment {
        case .right, .trailing: rect.origin.x += contentRect.width - contentBounds.contentSize.width
        case .center: rect.origin.x += (contentRect.width - contentBounds.contentSize.width) / 2
        case .fill:
            if self.imagePosition == .left {
                rect.origin.x += contentRect.width - contentBounds.contentSize.width
            }
        default: break
        }
        switch self.contentVerticalAlignment {
        case .bottom: rect.origin.y += contentRect.height - contentBounds.contentSize.height
        case .center: rect.origin.y += (contentRect.height - contentBounds.contentSize.height) / 2
        case .fill:
            if self.imagePosition == .top {
                rect.origin.y += contentRect.height - contentBounds.contentSize.height
            }
        default: break
        }
        return rect
    }
    open override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
        let contentBounds = self.contentBounds(for: contentRect.size)
        var rect = contentBounds.imageFrame
        rect.origin.x += contentRect.origin.x
        rect.origin.y += contentRect.origin.y
        switch self.contentHorizontalAlignment {
        case .right, .trailing: rect.origin.x += contentRect.width - contentBounds.contentSize.width
        case .center: rect.origin.x += (contentRect.width - contentBounds.contentSize.width) / 2
        case .fill:
            if self.imagePosition == .right {
                rect.origin.x += contentRect.width - contentBounds.contentSize.width
            }
        default: break
        }
        switch self.contentVerticalAlignment {
        case .bottom: rect.origin.y += contentRect.height - contentBounds.contentSize.height
        case .center: rect.origin.y += (contentRect.height - contentBounds.contentSize.height) / 2
        case .fill:
            if self.imagePosition == .bottom {
                rect.origin.y += contentRect.height - contentBounds.contentSize.height
            }
        default: break
        }
        return rect
    }
    open override var intrinsicContentSize: CGSize {
        return self.contentBounds().contentSize
    }
    open override func draw(_ rect: CGRect) {
        if (self.underlineWidth > 0) {
            let textRect = self.titleLabel?.frame ?? .zero
            if let context = UIGraphicsGetCurrentContext(), let color = self.currentUnderlineColor() {
                let descender = self.titleLabel?.font.descender ?? 0
                context.setStrokeColor(color.cgColor)
                context.setLineWidth(self.underlineWidth)
                let y = descender + 1 + self.underlineWidth / 2 + self.underlineInsets.top
                let x1 = self.underlineInsets.left
                let x2 = self.underlineInsets.left + self.underlineInsets.right
                context.move(to: CGPoint(x: textRect.origin.x + x1, y: textRect.origin.y + textRect.size.height + y))
                context.addLine(to: CGPoint(x: textRect.origin.x + textRect.size.width - x2, y: textRect.origin.y + textRect.size.height + y))
                
                context.closePath()
                context.drawPath(using: .stroke)
            }
        }
    }
    open func hitTestBounds() -> CGRect {
        var rect = self.bounds
        rect.origin.x -= self.hitEdgeInsets.left
        rect.origin.y -= self.hitEdgeInsets.top
        rect.size.width += self.hitEdgeInsets.left + self.hitEdgeInsets.right
        rect.size.height += self.hitEdgeInsets.top + self.hitEdgeInsets.bottom
        return rect
    }
    open override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        return self.hitTestBounds().contains(point)
    }
}


#if canImport(UICreate)
import UICreate

extension UCSetting where TargetType: FXButton {
    ///下划线
    public static func underline(_ color:UIColor? = nil, width: CGFloat = 1, inset: UIEdgeInsets = .zero) -> UCSetting<TargetType> {
        return .init(action: { (target) in
            target.underlineColor = color
            target.underlineWidth = width
            target.underlineInsets = inset
        })
    }
    ///图片位置
    public static func image(position:FXButton.Position) -> UCSetting<TargetType> {
        return .init(action: { (target) in
            target.imagePosition = position
        })
    }
    ///事件触发范围
    public static func hit(inset:UIEdgeInsets) -> UCSetting<TargetType> {
        return .init(action: { (target) in
            target.hitEdgeInsets = inset
        })
    }
}

#endif
